﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using FSMtoCode.Types;
using System.IO;

namespace FSMtoCode.Utils
{
    class CodeGenerator
    {
        private AST genCodeAST;
        private Symbols tkn;
        private String sPath;

        private const String sCommentAdvise = "//CODE AUTOMATICALLY GENERATED";
        private const String sNamespace = "CharacterEvents";
        private const String sMainClass = "Character";
        private const String sMainVarClass = "c";
        private const String sEventsVar = "Events";
        private const String sProcessMethod = "Process";
        private const String sInterface = "ICharacterStates";
        private const String sVarState = "State";
        private const String sReportState = "ReportState()";
        private const String sQueueDec = "Queue<Object> ";

        private String sIndLvl1 = "\t";
        private String sIndLvl2 = "\t\t";
        private String sIndLvl3 = "\t\t\t";
        private String sIndLvl4 = "\t\t\t\t";

        public CodeGenerator(AST ast)
        {
            this.genCodeAST = ast;
            this.tkn = new Symbols();
        }

        public void GenerateClasses()
        {
            sPath = CreateSubFolder();
            GenerateTriggerClasses();
            GenerateStateClasses();
        }

        private String CreateSubFolder()
        {
            //Specify a "currently active folder"
            String activeDir = @"c:\";

            //Create a new subfolder under the current active folder
            String sPath = System.IO.Path.Combine(activeDir, "FSMToCode");
            System.IO.Directory.CreateDirectory(sPath);

            return sPath; 
        }

        private void GenerateStateClasses()
        {
            String sStateFile = sPath + "\\States.cs";
            String sNameGuard = "true";
            String sFinalStateName = "";
            int iCountStruct = 1;

            List<String> guardsCreated = new List<String>();

            FileStream fs = null;
            fs = File.Create(sStateFile);
            fs.Close();
            
            if (fs != null)
            {
                using (StreamWriter sw = new StreamWriter(sStateFile))
                {
                    sw.WriteLine(tkn.Header);

                    sw.WriteLine(sCommentAdvise);

                    //namespace ICharacterEvents {
                    sw.WriteLine(tkn.Namespace + sNamespace);
                    sw.WriteLine(tkn.AbreChave);

                    #region [ICharacterState]
                    //interface ICharacterState {}
                    sw.WriteLine(sIndLvl1 + tkn.Public + tkn.Interface + sInterface);
                    sw.WriteLine(sIndLvl1 + tkn.AbreChave);
                    sw.WriteLine(sIndLvl2 + tkn.Void + sProcessMethod + "(" + sMainClass + " " + sMainVarClass + tkn.Virgula + sQueueDec + sEventsVar + ")" + tkn.PtoVirgula);
                    sw.WriteLine(sIndLvl2 + tkn.Void + sReportState + tkn.PtoVirgula);
                    sw.WriteLine(sIndLvl1 + tkn.FechaChave);
                    #endregion

                    #region [Character Class]
                    //public class Character{}
                    sw.WriteLine(sIndLvl1 + tkn.Public + tkn.Class + "Character");
                    sw.WriteLine(sIndLvl1 + tkn.AbreChave);

                    //Var State and Events{get set} 
                    sw.WriteLine(sIndLvl2 + tkn.Public + sInterface + " " + sVarState + tkn.AbreChave + tkn.Get + tkn.PtoVirgula + tkn.Set + tkn.PtoVirgula + tkn.FechaChave);
                    sw.WriteLine(sIndLvl2 + tkn.Public + sQueueDec + sEventsVar + tkn.AbreChave + tkn.Get + tkn.PtoVirgula + tkn.Set + tkn.PtoVirgula + tkn.FechaChave);

                    //Constructor
                    sw.WriteLine(sIndLvl2 + tkn.Public + sMainClass + "()");
                    sw.WriteLine(sIndLvl2 + tkn.AbreChave);
                    sw.WriteLine(sIndLvl3 + sEventsVar + tkn.Igual + tkn.New + sQueueDec + "()" + tkn.PtoVirgula);
                    sw.WriteLine(sIndLvl2 + tkn.FechaChave);

                    //Method Process                   
                    sw.WriteLine(sIndLvl2 + tkn.Public + tkn.Void + sProcessMethod + "()");
                    sw.WriteLine(sIndLvl2 + tkn.AbreChave);
                    sw.WriteLine(sIndLvl3 + "State" + tkn.Pto + sProcessMethod + "(this,this.Events);");
                    sw.WriteLine(sIndLvl2 + tkn.FechaChave);

                    //Method ReportState
                    sw.WriteLine(sIndLvl2 + tkn.Public + tkn.Void + sReportState);
                    sw.WriteLine(sIndLvl2 + tkn.AbreChave);
                    sw.WriteLine(sIndLvl3 + "State" + tkn.Pto + sReportState + tkn.PtoVirgula);
                    sw.WriteLine(sIndLvl2 + tkn.FechaChave);

                    sw.WriteLine(sIndLvl1 + tkn.FechaChave);
                    #endregion

                    #region [States]
                    foreach (FSMState state in genCodeAST.getlstStates)
                    {
                        //Create events classes declarations
                        sw.WriteLine(sIndLvl1 + tkn.Public + tkn.Class + state.Name + ":CharacterEvents." + sInterface);
                        sw.WriteLine(sIndLvl1 + tkn.AbreChave);
                        sw.WriteLine(sIndLvl2 + tkn.Public + tkn.Void + sProcessMethod + "(" + sMainClass + " " + sMainVarClass + tkn.Virgula + sQueueDec + sEventsVar + ")");
                        sw.WriteLine(sIndLvl2 + tkn.AbreChave);
                    
                        sw.WriteLine("");
                        sw.WriteLine(sIndLvl3 + "Object e = " + sEventsVar + tkn.Pto + "Dequeue()" + tkn.PtoVirgula);
                        sw.WriteLine(String.Empty); 
                          
                          foreach (FSMTransition transition in genCodeAST.getlstTransitions) // Loop through List with foreach
                          {
                              if (transition.ConnecFrom.Equals(state.Id))
                              {
                                  if (!transition.Guard.Equals(""))
                                  {
                                      sNameGuard = transition.Guard + "()";  
                                      guardsCreated.Add(transition.Guard);
                                  }   
                               
                                  if (iCountStruct == 1)
                                  {
                                      sw.WriteLine(sIndLvl3 + "if(e is " + transition.Trigger + " && " + sNameGuard + ")");
                                      iCountStruct++;
                                  }
                                  else
                                  {
                                      sw.WriteLine(sIndLvl3 + "else if(e is " + transition.Trigger + " && " + sNameGuard + ")");
                                  }

                                  sw.WriteLine(sIndLvl4 + sMainVarClass + tkn.Pto + sVarState + tkn.Igual + tkn.New + transition.Action + "()" + tkn.PtoVirgula);
                              }

                              sNameGuard = "true";
                          }
                            
                          //Reset the count struct for the next state
                          iCountStruct = 1;                          
                          
                        //Close method Process
                        sw.WriteLine(sIndLvl2 + tkn.FechaChave);

                        sw.WriteLine(String.Empty);

                        //Create Report implementation
                        sw.WriteLine(sIndLvl2 + tkn.Public + tkn.Void + sReportState);
                        sw.WriteLine(sIndLvl2 + tkn.AbreChave);
                        sw.WriteLine(sIndLvl3 + "Console.WriteLine(\"" + "State " + state.Name + "\")" + tkn.PtoVirgula);
                        sw.WriteLine(sIndLvl2 + tkn.FechaChave);
                      
                        //Guard generation methods
                        if (guardsCreated.Count > 0)
                        {
                            foreach (String sGuardName in guardsCreated)
                            {
                                sw.WriteLine(sIndLvl2 + tkn.Public + "Boolean " + sGuardName + "()");
                                sw.WriteLine(sIndLvl2 + tkn.AbreChave);
                                sw.WriteLine(sIndLvl3 + "return true;");
                                sw.WriteLine(sIndLvl2 + tkn.FechaChave);
                                sw.WriteLine(String.Empty);
                            }
                        }

                        guardsCreated.Clear();
                        //Close class
                        sw.WriteLine(sIndLvl1 + tkn.FechaChave);
                    }
                    #endregion

                    #region [StatesTerm]
                    foreach(FSMStateTerm stateTerm in genCodeAST.getlstStatesTerm)
                    {
                        if (stateTerm.Id.Equals("O0") && !stateTerm.IsFinal)
                        {
                            //Create events classes declarations
                            sw.WriteLine(sIndLvl1 + tkn.Public + tkn.Class + "StartNPC" + ":" + sInterface);
                            sw.WriteLine(sIndLvl1 + tkn.AbreChave);
                            sw.WriteLine(sIndLvl2 + tkn.Public + tkn.Void + sProcessMethod + "(" + sMainClass + " " + sMainVarClass + tkn.Virgula + sQueueDec + sEventsVar + ")");
                            sw.WriteLine(sIndLvl2 + tkn.AbreChave);

                            foreach (FSMTransition transition in genCodeAST.getlstTransitions) // Loop through List with foreach
                            {
                                if (transition.ConnecFrom.Equals("O0"))
                                    sw.WriteLine(sIndLvl3 + sMainVarClass + tkn.Pto + sVarState + tkn.Igual + tkn.New + transition.Action + "()" + tkn.PtoVirgula);

                                if (transition.ConnecTo.Equals("O1"))
                                    sFinalStateName = transition.Action;
                            }

                            sw.WriteLine(sIndLvl2 + tkn.FechaChave);

                            //Create Report implementation
                            sw.WriteLine(sIndLvl2 + tkn.Public + tkn.Void + sReportState);
                            sw.WriteLine(sIndLvl2 + tkn.AbreChave);
                            sw.WriteLine(sIndLvl3 + "Console.WriteLine(\"" + "State StartNPC\")" + tkn.PtoVirgula);
                            sw.WriteLine(sIndLvl2 + tkn.FechaChave);

                            sw.WriteLine(sIndLvl1 + tkn.FechaChave);
                        }
                        else 
                        {
                            //Create events classes declarations
                            sw.WriteLine(sIndLvl1 + tkn.Public + tkn.Class + sFinalStateName + ":" + sInterface);
                            sw.WriteLine(sIndLvl1 + tkn.AbreChave);
                            sw.WriteLine(sIndLvl2 + tkn.Public + tkn.Void + sProcessMethod + "(" + sMainClass + " " + sMainVarClass + tkn.Virgula + sQueueDec + sEventsVar + ")");
                            sw.WriteLine(sIndLvl2 + tkn.AbreChave);
                            sw.WriteLine(sIndLvl3 + sMainVarClass + tkn.Pto + sVarState + tkn.Igual + tkn.New + sFinalStateName + "()" + tkn.PtoVirgula);
                            sw.WriteLine(sIndLvl2 + tkn.FechaChave);

                            //Create Report implementation
                            sw.WriteLine(sIndLvl2 + tkn.Public + tkn.Void + sReportState);
                            sw.WriteLine(sIndLvl2 + tkn.AbreChave);
                            sw.WriteLine(sIndLvl3 + "Console.WriteLine(\"" + "State End\")" + tkn.PtoVirgula);
                            sw.WriteLine(sIndLvl2 + tkn.FechaChave);

                            sw.WriteLine(sIndLvl1 + tkn.FechaChave);
                        }
                    }                    
                    
                    #endregion

                    //Close Namespace
                    sw.WriteLine(tkn.FechaChave);
                }                    
            }
        }

        public void GenerateTriggerClasses() 
        {
            List<String> transCreated = new List<String>();

            String sTriggerFile = sPath + "\\Events.cs";
            String transParam = "(";

            FileStream fs = null;
            fs = File.Create(sTriggerFile);
            fs.Close();
            
            if (fs != null){
                using (StreamWriter sw = new StreamWriter(sTriggerFile))
                {
                    sw.WriteLine(tkn.Header);

                    sw.WriteLine(sCommentAdvise);

                    //namespace CharacterEvents {
                    sw.WriteLine(tkn.Namespace + sNamespace);
                    sw.WriteLine(tkn.AbreChave);
                  
                    //interface IEvents {}
                    sw.WriteLine(sIndLvl1 + tkn.Interface + "IEvents " + tkn.AbreChave + tkn.FechaChave);
                   
                    //public class InimigoProx:IEvent{ 


                    foreach (FSMTransition transition in genCodeAST.getlstTransitions) // Loop through List with foreach
                    
                    {
                        //If the event was not created before
                        if (!transCreated.Contains(transition.Trigger))
                        {
                            //Create events classes declarations
                            sw.WriteLine(sIndLvl1 + tkn.Public + tkn.Class + transition.Trigger + ":IEvents");
                            sw.WriteLine(sIndLvl1 + tkn.AbreChave);

                            //Create variables declarations
                            foreach (String sVar in transition.TriggerParam)
                                sw.WriteLine(sIndLvl2 + tkn.Private + sVar + tkn.AbreChave + tkn.Get + tkn.PtoVirgula + tkn.Set + tkn.PtoVirgula + tkn.FechaChave);                                
                          
                            if (transition.TriggerParam.Count != 0)
                            {                                
                                for (int c = 0; c < transition.TriggerParam.Count; c++) 
                                {
                                    if(c < (transition.TriggerParam.Count-1))
                                        transParam = transParam + transition.TriggerParam.ElementAt(c) + tkn.Virgula;
                                    else
                                        transParam = transParam + transition.TriggerParam.ElementAt(c) + ")";
                                }

                                sw.WriteLine(sIndLvl2 + tkn.Public + transition.Trigger + transParam);
                                sw.WriteLine(sIndLvl2 + tkn.AbreChave);

                                //Create constructors
                                for (int c = 0; c < transition.TriggerParam.Count; c++)
                                {
                                    transParam = GetTriggerVarName(transition.TriggerParam.ElementAt(c));
                                    sw.WriteLine(sIndLvl3 + tkn.This + tkn.Pto + transParam + tkn.Igual + transParam + tkn.PtoVirgula);
                                }
                                //Close the contructor
                                sw.WriteLine(sIndLvl2 + tkn.FechaChave);
                            }

                            sw.WriteLine(sIndLvl1 + tkn.FechaChave);
                            transCreated.Add(transition.Trigger);
                        }
                        transParam = "(";
                    }
                    
                    sw.WriteLine(tkn.FechaChave);
                }
            }                   
        }

        private String GetTriggerVarName(String TriggerParam)
        {
            if (TriggerParam.Contains("double"))
                TriggerParam = TriggerParam.Substring((TriggerParam.IndexOf("e") + 2));
            else if (TriggerParam.Contains("int"))
                TriggerParam = TriggerParam.Substring((TriggerParam.IndexOf("t") + 2));
            else if (TriggerParam.Contains("long"))
                TriggerParam = TriggerParam.Substring((TriggerParam.IndexOf("g") + 2));
            else if (TriggerParam.Contains("Boolean"))
                TriggerParam = TriggerParam.Substring((TriggerParam.IndexOf("n") + 2));
            else if (TriggerParam.Contains("string"))
                TriggerParam = TriggerParam.Substring((TriggerParam.IndexOf("g") + 2));

            return TriggerParam;
        }
       
    }
}

